#include "linux/platform_device.h"
#include "linux/irqchip.h"
#include "linux/generic-chip.h"
#include "linux/slab.h"
#include "linux/irq.h"
#include "linux/irqdomain.h"
#include "linux/kernel.h"
#include "linux/types.h"
#include "linux/init.h"
#include "linux/generic-chip.h"
#include "linux/interrupt.h"
#include "linux/irq.h"

struct mxc_gpio_port
{
    struct list_head node;
    //struct clk *clk;
    void __iomem *base;
    int irq;
    int irq_high;
    struct irq_domain *domain;
    //struct gpio_chip gc;
    u32 both_edges;
    int saved_reg[6];
    int gpio_ranges;
};

static struct of_device_id mxc_gpio_dt_ids[] = {
	{ .compatible = "fsl,imx6ul-gpio", .data =  NULL },
	{ /* sentinel */ }
};

static int gpio_set_irq_type(struct irq_data *d, u32 type)
{
    return 0;
}

static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
{

    return 0;
}

static int imx6u_gpio_irq_reqres(struct irq_data *d)
{
    return 0;
}

static void imx6u_gpio_irq_relres(struct irq_data *d)
{

}

static int imx6u_gpio_init_gc(struct mxc_gpio_port *port, int irq_base, struct device *dev)
{
    struct irq_chip_generic *gc = NULL;
    struct irq_chip_type *ct;

    gc = irq_alloc_generic_chip("gpio-imx6u", 1, irq_base, port->base, handle_level_irq);
    if (!gc)
        return -ENOMEM;
    gc->private = port;

    ct = gc->chip_types;
    //ct->chip.parent_device = dev;
    ct->chip.irq_ack = irq_gc_ack_set_bit;
    ct->chip.irq_mask = irq_gc_mask_clr_bit;
    ct->chip.irq_unmask = irq_gc_mask_set_bit;
    ct->chip.irq_set_type = gpio_set_irq_type;
    ct->chip.irq_set_wake = gpio_set_wake_irq;
    ct->chip.irq_request_resources = imx6u_gpio_irq_reqres;
    ct->chip.irq_release_resources = imx6u_gpio_irq_relres,
    ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND;
    //ct->regs.ack = GPIO_ISR;
    //ct->regs.mask = GPIO_IMR;

    irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0);

    return 0;
}

static void imx6_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
    u32 irq_stat = 0xff;
    struct mxc_gpio_port *port = desc->irq_common_data.handler_data;

    //irq_stat = readl(port->base + GPIO_ISR) & readl(port->base + GPIO_IMR);

    while (irq_stat != 0)
    {
        int irqoffset = 18;// fls(irq_stat) - 1;


		generic_handle_irq(irq_find_mapping(port->domain, irqoffset));

		irq_stat &= ~(1 << irqoffset);
    }
}

static int imx6u_gpio_probe(struct platform_device *pdev)
{
    int err;
    struct device_node *np = pdev->dev.of_node;
    struct mxc_gpio_port *port;
    int irq_base;

    port = kzalloc(sizeof(*port), GFP_KERNEL);
    if (!port)
        return -ENOMEM;

    port->irq_high = platform_get_irq(pdev, 1);
    port->irq = platform_get_irq(pdev, 0);
    irq_base = irq_alloc_descs(-1, 0, 32, -1);

    irq_set_chained_handler_and_data(port->irq, imx6_gpio_irq_handler, port);

    port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, &irq_domain_simple_ops, NULL);
    if (!port->domain)
    {
        err = -ENODEV;
        goto out_irqdesc_free;  
    }

    imx6u_gpio_init_gc(port, irq_base, &pdev->dev);

    return 0;

out_irqdesc_free:
    irq_free_descs(irq_base, 32);

    return err;
}

static struct platform_driver imx6u_gpio_driver = {
    .driver = 
    {
        .name = "imx6ul-gpio",
        .of_match_table = mxc_gpio_dt_ids,
    },
    .probe = imx6u_gpio_probe,
};

static int __init gpio_mxc_init(void)
{
	return platform_driver_register(&imx6u_gpio_driver);
}

subsys_initcall(gpio_mxc_init);
